#ifndef HANDLER_H_
#define HANDLER_H_

#include <GL/glut.h>
#include <sigc++/signal.h>

class Handler {
   private:
      // key no-especial, x, y
      sigc::signal<void, unsigned char, int, int> _signal_keyDown;
      sigc::signal<void, unsigned char, int, int> _signal_keyUp;
      sigc::signal<void, unsigned char, int, int, bool> _signal_key;

      // key especial, x, y
      sigc::signal<void, int, int, int> _signal_specialkeyDown;
      sigc::signal<void, int, int, int> _signal_specialkeyUp;
      sigc::signal<void, int, int, int, bool> _signal_specialkey;

      // boton (izq/medio/der), estado (presionado/liberado), x, y
      sigc::signal<void, int, int, int, int> _signal_mouse;

      // x, y, dx, dy
      sigc::signal<void, int, int, int, int> _signal_grab;

      // x, y, dx, dy
      sigc::signal<void, int, int, int, int> _signal_move;



      // Myself
      static Handler *myself;

      static void handler_keyDown(unsigned char k, int x, int y) {
         myself->_signal_keyDown.emit(k, x, y);
         myself->_signal_key.emit(k, x, y, true);
      }
      static void handler_keyUp(unsigned char k, int x, int y) {
         myself->_signal_keyUp.emit(k, x, y);
         myself->_signal_key.emit(k, x, y, false);
      }
      static void handler_specialkeyDown(int k, int x, int y) {
         myself->_signal_specialkeyDown.emit(k, x, y);
         myself->_signal_specialkey.emit(k, x, y, true);
      }
      static void handler_specialkeyUp(int k, int x, int y) {
         myself->_signal_specialkeyUp.emit(k, x, y);
         myself->_signal_specialkey.emit(k, x, y, false);
      }
      static void handler_mouse(int boton, int estado, int x, int y) {
         myself->_signal_mouse.emit(boton, estado, x, y);
      }
      static void handler_grab(int x, int y) {
         static int antX = x;
         static int antY = y;
         myself->_signal_grab.emit(x, y, x-antX, y-antY);
         antX = x;
         antY = y;
      }
      static void handler_move(int x, int y) {
         static int antX = x;
         static int antY = y;
         myself->_signal_move.emit(x, y, x-antX, y-antY);
         antX = x;
         antY = y;
      }


   public:

      void asignarManejadores() {
         myself = this;
         glutKeyboardFunc(&Handler::handler_keyDown);
         glutKeyboardUpFunc(&Handler::handler_keyUp);
         glutSpecialFunc(&Handler::handler_specialkeyDown);
         glutSpecialUpFunc(&Handler::handler_specialkeyUp);
         glutMouseFunc(&Handler::handler_mouse);
         glutMotionFunc(&Handler::handler_grab);
         glutPassiveMotionFunc(&Handler::handler_move);
      }

      /*
       * La signal es activada cuando una tecla no especial (alfa numerica)
       * es presionada.
       * La tecla en cuestion es pasada como primer argumento.
       * Los siguientes dos argumentos template corresponden a la
       * posicion x e y del mouse.
       * */
      sigc::signal<void, unsigned char, int, int>& signal_keyDown() {
         return _signal_keyDown;
      }
      sigc::signal<void, unsigned char, int, int>& signal_keyUp() {
         return _signal_keyUp;
      }
      /*
       * Version combinada de keyDown y keyUp.
       * El ultimo parametro es un booleano con valor True si la
       * tecla fue presionada (Down) o False si fue liberada (Up)
       * */
      sigc::signal<void, unsigned char, int, int, bool>& signal_key() {
         return _signal_key;
      }

      /*
       * Similar a la signal_key solo que en vez de observar teclas
       * no especiales, toma en cuenta teclase especiales.
       * */
      sigc::signal<void, int, int, int>& signal_specialkeyDown() {
         return _signal_specialkeyDown;
      }
      sigc::signal<void, int, int, int>& signal_specialkeyUp() {
         return _signal_specialkeyUp;
      }
      /*
       * Version combinada de specialkeyDown y specialkeyUp.
       * El ultimo parametro es un booleano con valor True si la
       * tecla fue presionada (Down) o False si fue liberada (Up)
       * */
      sigc::signal<void, int, int, int, bool>& signal_specialkey() {
         return _signal_specialkey;
      }

      /*
       * Un evento de mouse es detectado, ya sea por que se presiono o se
       * libero un boton.
       * La accion en cuestion es pasado como segundo parametro,
       * y el primero indica que boton es el afectado.
       * Los ultimos dos parametros indican la posicion x e y del mouse.
       * */
      sigc::signal<void, int, int, int, int>& signal_mouse() {
         return _signal_mouse;
      }

      /*
       * El mouse se mueve mientras mantiene presionado uno o varios 
       * botones.
       * Los parametros pasados indican la posicion x e y del mouse.
       * El resto son los movimientos relativos respecto a la ultima
       * posicion.
       * */
      sigc::signal<void, int, int, int, int>& signal_grab() {
         return _signal_grab;
      }

      /*
       * Similar a signal_grab, solo que no hay ningun boton del
       * mouse presionado.
       * El resto son los movimientos relativos respecto a la ultima
       * posicion.
       * */
      sigc::signal<void, int, int, int, int>& signal_move() {
         return _signal_move;
      }
};

#endif
